#!/usr/bin/env python
# coding=utf-8
# Filename : DataCleaner.py
# Created by iFantastic on 2017/7/25
# Description : 数据清洗
import pandas as pd
import numpy as np
import pickle
import time
import os


def get_current_time():
    """
    以固定格式打印当前时间

    :return:返回当前时间的字符串

    :return:
    """
    return time.strftime('%Y-%m-%d %X', time.localtime())


class DataCleaner:
    """
    数据清洗器

    它的初始化需要提供三个文件的文件名。它提供了唯一的对外接口：load_data()。它返回清洗好的数据。
    如果数据已存在，则直接返回。否则将执行一系列的清洗操作，并返回清洗好的数据。
    """
    def __init__(self, people_filename, act_train_filename, act_test_filename):
        """

        :param people_filename: people.csv文件的file_path
        :param act_train_filename: act_train.csv文件的file_path
        :param act_test_filename: act_test.csv文件的file_path
        :return:
        """
        self.p_fname = people_filename
        self.train_fname = act_train_filename
        self.test_fname = act_test_filename
        self.types = ['type %d' %i for i in range(1, 8)]
        self.fname = 'output/cleaned_data'

    def load_data(self):
        """
        加载清洗好的数据

        如果数据已存在，则直接返回。如果不存在，则加载csv文件，然后合并数据、拆分成type1-type7，然后执行数据类型转换，最后重新排列每个列的顺序。再然后保存数据并返回
        :return:一个元组：依次为：self.train_datas, self.test_datas
        """
        if(self._is_ready()):
            print('cleaned data is available!\n')
            self._load_data()
        else:
            self._load_csv()
            self._merge_data()
            self._split_data()
            self._typecast_data()
            self._save_data()
        return self.train_datas, self.test_datas

    def _load_csv(self):
        """
        加载csv文件

        :return:
        """
        print('-----Begin run load_csv at %s ------' % get_current_time())
        self.people = pd.read_csv(self.p_fname, sep=',', header=0, keep_default_na=True, parse_dates=['date'])
        self.act_train = pd.read_csv(self.train_fname, sep=',', header=0, keep_default_na=True, parse_dates=['date'])
        self.act_test = pd.read_csv(self.test_fname, sep=',', header=0, keep_default_na=True, parse_dates=['date'])

        self.people.set_index(keys=['people_id'], drop=True, append=False, inplace=True)
        self.act_train.set_index(keys=['people_id'], drop=True, append=False, inplace=True)
        self.act_test.set_index(keys=['people_id'], drop=True, append=False, inplace=True)

    def _merge_data(self):
        """
        合并people数据和activity数据

        :return:
        """
        print('----- Begin run merge_data at %s ------' % get_current_time())
        self.train_data = self.act_train.merge(self.people, how='left', left_index=True, right_index=True, suffixes=('_act', '_people'))
        self.test_data = self.act_train.merge(self.people, how='left', left_index=True, right_index=True, suffixes=('_act', '_people'))
        print('----- End run merge_data at %s ------' % get_current_time())

    def _split_data(self):
        """
        拆分数据为type1-7

        :return:
        """
        print('----- Begin run split_data at %s ------' % get_current_time())
        self.train_datas = {}
        self.test_datas = {}
        for _type in self.types:
            self.train_datas[_type] = self.train_data[self.train_data.activity_category == _type].dropna(axis=(0, 1), how='all')
            self.test_datas[_type] = self.test_data[self.test_data.activity_category == _type].dropna(axis=(0, 1), how='all')
            # 对于各自的数据集该项属性都一样，冗余去掉
            self.train_datas[_type].drop(['activity_category'], axis=1, inplace=True)
            self.test_datas[_type].drop(['activity_category'], axis=1, inplace=True)
            # 建立(people_id, activity_id)唯一索引
            self.train_datas[_type].set_index(keys=['activity_id'], drop=True, append=True, inplace=True)
            self.test_datas[_type].set_index(keys=['activity_id'], drop=True, append=True, inplace=True)
        print('----- End run split_data at %s ------' % get_current_time())

    def _typecast_data(self):
        """
        执行数据类型转换，将所有数据转换为浮点数

        :return:
        """
        print('----- Begin run typecast_data at %s ------' % get_current_time())
        str_col_list = ['group_1']+['char_%d_act' %i for i in range(1, 11)]+['char_%d_people' %i for i in range(1, 10)]
        bool_col_list = ['char_10_people']+['char_%d' %i for i in range(11, 38)]
        for _type in self.types:
            for data_set in [self.train_datas, self.test_datas]:
                # 处理日期列
                data_set[_type].date_act = (data_set[_type].date_act - np.datetime64('1970-01-01')) / np.timedelta64(1, 'D')
                data_set[_type].date_people = (data_set[_type].date_people - np.datetime64('1970-01-01')) / np.timedelta64(1, 'D')
                # 处理group ***列
                data_set[_type].group_1 = data_set[_type].group_1.str.replace('group', '').str.strip().astype(np.float64)
                # 处理布尔值列
                for col in bool_col_list:
                    if col in data_set[_type]:
                        data_set[_type][col] = data_set[_type][col].astype(np.float64)
                # 处理其他字符串列
                for col in str_col_list[1:]:
                    if col in data_set[_type]:
                        data_set[_type][col] = data_set[_type][col].str.replace('type', '').str.strip().astype(np.float64)
                data_set[_type] = data_set[_type].astype(np.float64)
        print('----- End run typecast_data at %s ------' % get_current_time())

    def _is_ready(self):
        if(os.path.exists(self.fname)):
            return True
        else:
            return False

    def _save_data(self):
        print('----- Begin run save_data at %s ------' % get_current_time())
        with open(self.fname, 'wb') as file:
            pickle.dump([self.train_datas, self.test_datas], file=file)
        print('----- End run save_data at %s ------' % get_current_time())

    def _load_data(self):
        print('----- Begin run _load_data at %s ------' % get_current_time())
        print(self.fname)
        with open(self.fname, 'rb') as file:
            self.train_datas, self.test_datas = pickle.load(file)
        print('----- End run _load_data at %s ------' % get_current_time())
